using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Text;
using PS.WebParts.Rollup.Utils;

namespace PS.WebParts.Rollup.Query
{
    public enum SID_NAME_USE
    {
        SidTypeAlias = 0x4,
        SidTypeComputer = 0x9,
        SidTypeDeletedAccount = 0x6,
        SidTypeDomain = 0x3,
        SidTypeGroup = 0x2,
        SidTypeInvalid = 0x7,
        SidTypeUnknown = 0x8,
        SidTypeUser = 0x1,
        SidTypeWellKnownGroup = 0x5
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct SID_AND_ATTRIBUTES
    {
        public IntPtr Sid;
        public int Attributes;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct TOKEN_GROUPS
    {
        public uint GroupCount;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1000)]
        public SID_AND_ATTRIBUTES[] Groups;
        //public IntPtr Groups;
    }

    enum TOKEN_INFORMATION_CLASS
    {
        TokenUser = 1,
        TokenGroups,
        TokenPrivileges,
        TokenOwner,
        TokenPrimaryGroup,
        TokenDefaultDacl,
        TokenSource,
        TokenType,
        TokenImpersonationLevel,
        TokenStatistics,
        TokenRestrictedSids,
        TokenSessionId,
        TokenGroupsAndPrivileges,
        TokenSessionReference,
        TokenSandBoxInert,
        TokenAuditPolicy,
        TokenOrigin
    }

    internal class CurrentUser
    {
        [DllImport("advapi32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool OpenProcessToken(IntPtr ProcessHandle, uint DesiredAccess, out IntPtr TokenHandle);

        [DllImport("kernel32.dll")]
        static extern IntPtr GetCurrentProcess();

        [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true, EntryPoint = "LookupAccountSid")]
        internal static extern bool LookupAccountSidNative(string SystemName, IntPtr Sid, StringBuilder Name,
            ref int cbName, StringBuilder DomainName,
            ref int cbDomainName, ref SID_NAME_USE peUse);

        [DllImport("advapi32.dll", SetLastError = true)]
        static extern bool GetTokenInformation(
            IntPtr TokenHandle,
            TOKEN_INFORMATION_CLASS TokenInformationClass,
            IntPtr TokenInformation,
            int TokenInformationLength,
            out int ReturnLength);

        private const int TOKEN_QUERY = 0x0008;

        private static string LookupAccountByStringSid(IntPtr sid)
        {
            string res;

            SID_NAME_USE sidTypeUnknown = SID_NAME_USE.SidTypeUnknown;
            int capacity = 0x100;
            StringBuilder domainName = new StringBuilder(capacity);
            int num2 = 0x100;
            StringBuilder name = new StringBuilder(num2);
            if (!LookupAccountSidNative(null, sid, name, ref num2, domainName, ref capacity, ref sidTypeUnknown))
            {
                throw new Win32Exception(Marshal.GetLastWin32Error());
            }

            if (domainName.ToString().ToUpperInvariant() == "BUILTIN")
            {
                domainName = new StringBuilder(Environment.MachineName);
            }
            res = domainName + @"\" + name;

            return res;
        }

        public static List<string> GetDomainGroups()
        {
            List<string> result = new List<string>();

            int tokenInfoLength = 0;
            //byte[] token = user.GetBinaryId();
            //IntPtr tokenPtr = Marshal.AllocHGlobal(token.Length);
            //Marshal.Copy(token, 0, tokenPtr, token.Length);

            IntPtr threadToken;
            bool res = OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, out threadToken);
            if (res)
            {
                GetTokenInformation(threadToken,
                                    TOKEN_INFORMATION_CLASS.TokenGroups,
                                    IntPtr.Zero,
                                    tokenInfoLength,
                                    out tokenInfoLength);

                IntPtr tokenInfo = Marshal.AllocHGlobal(tokenInfoLength);
                res = GetTokenInformation(threadToken,
                                          TOKEN_INFORMATION_CLASS.TokenGroups,
                                          tokenInfo,
                                          tokenInfoLength,
                                          out tokenInfoLength);
                if (res)
                {
                    try
                    {
                        TOKEN_GROUPS groups = (TOKEN_GROUPS) Marshal.PtrToStructure(tokenInfo, typeof (TOKEN_GROUPS));
                        for (int i = 0; i < groups.GroupCount; i++)
                        {
                            SID_AND_ATTRIBUTES group = groups.Groups[i];
                            try
                            {
                                string account = LookupAccountByStringSid(group.Sid);
                                result.Add(account);
                            }
                            catch (Win32Exception)
                            {
                            }
                        }
                    }
                    catch (AccessViolationException)
                    {
                    }
                }

                Marshal.FreeHGlobal(tokenInfo);
            }

            return result;
        }

        public static string GetDomainGroupsCSV()
        {
            return StringUtil.ArrayToCSV(GetDomainGroups());
        }
    }
}
